package tsing.good_simple.abstractLock.singleProjectAbstract;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Date 2024/11/25 15:01
 * @Author Tsing
 * 使用AOP注解加锁的实现方式
 * @LockkAnnotation 的处理器
 */
@Aspect
@Component
@Order(1)
public class LockAnnotationAspect {

    private static final Logger log = LoggerFactory.getLogger(LockAnnotationAspect.class);



    private final Cache<String, Lock> lockCache;

    // 使用guava的缓存工具
    public LockAnnotationAspect (){
        lockCache = CacheBuilder.newBuilder()
                .expireAfterAccess(10,TimeUnit.MINUTES)
                .build();
    }

    @Pointcut("@annotation(tsing.good_simple.abstractLock.singleProjectAbstract.LockAnnotation)")  //通过注解定义切点
    public void pointCut() {

    }

    @Around("pointCut()")  //环绕通知
    public Object doArround(ProceedingJoinPoint point) throws Throwable {
        log.info("进入aop");
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature)point.getSignature();
        // 拿到方法
        Method method = methodSignature.getMethod();
        // 拿到方法上的注解
        LockAnnotation annotation = method.getAnnotation(LockAnnotation.class);
        // 获取注解上的内容
        long timeOut = annotation.timeOut();
        TimeUnit unit = annotation.unit();
        String key = annotation.key();
        // 获取锁 or 初始化锁
        Lock lock = lockCache.getIfPresent(key);
        if(lock==null){
            synchronized (this){
                lock = lockCache.getIfPresent(key);
                if (lock == null) {
                    lock = new ReentrantLock();
                    lockCache.put(key, lock);
                }
            }

        }
        boolean lockFlag = lock.tryLock(timeOut, unit);
        if(!lockFlag){
            // or throw ex;
            return null;
        }
        try {
            // 加锁成功,执行业务方法
            return  point.proceed();
        } finally {
            // 解锁
            lock.unlock();
        }



    }
}
